package com.triones.api.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.EmailCodeTypeConstants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DeviceType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.email.EmailValidationUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.triones.api.entity.*;
import com.triones.api.entity.bo.LoginBO;
import com.triones.api.entity.bo.MemberBO;
import com.triones.api.entity.bo.MemberInfoBO;
import com.triones.api.entity.vo.MemberVO;
import com.triones.api.mapper.*;
import com.triones.api.service.LogininforService;
import com.triones.api.service.third.LeptageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;

/**
 * 登录、注册、验证 -业务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LogininforServiceImpl implements LogininforService {

    private final MemberMapper memberMapper;

    private final InviteRecordMapper inviteRecordMapper;

    private final CcyAndChainDictMapper ccyAndChainDictMapper;

    private final CryptoLinkSourceMapper cryptoLinkSourceMapper;

    private final CryptoWalletMapper cryptoWalletMapper;

    private final FaitWalletMapper faitWalletMapper;

    private final FaitDictMapper faitDictMapper;

    private final LeptageService leptageService;

    @Async
    @Override
    public void createEmailCode(String type, String email) {
        log.info("邮箱验证码进入时间=》" + System.currentTimeMillis());
        /**
         * 临时使用
         */
//        String key = type + email;
//        long timeToLive = RedisUtils.getTimeToLive(key);
//        if (timeToLive >= (Constants.CAPTCHA_EXPIRATION * 60 - 60)) {
//            throw new ServiceException("请勿频繁发送验证码");
//        }
//        String code = "123456";

        /**
         * 正式使用
         */
        String key = type + email;
        long timeToLive = RedisUtils.getTimeToLive(key);
        if (timeToLive >= (Constants.CAPTCHA_EXPIRATION * 60 - 60)) {
            throw new ServiceException("请勿频繁发送验证码");
        }
        String code = RandomUtil.randomNumbers(6);
        try {
            log.info("邮箱验证码发起时间=》" + System.currentTimeMillis());
            GodaddyCommonsEmailSender.sendCode(email,code);
            log.info("邮箱验证码结束时间=》" + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("Mail sending failed");
        }
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
        log.info("邮箱验证码结束时间=》" + System.currentTimeMillis());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean register(MemberBO bo) {
        MemberDO memberDO = memberMapper.selectOne(new LambdaQueryWrapper<MemberDO>()
            .eq(MemberDO::getEmail, bo.getEmail())
            .in(MemberDO::getStatus, "0", "1"));
        if (memberDO != null) {
            throw new ServiceException("邮箱已被注册");
        }
        long timeToLive = RedisUtils.getTimeToLive(EmailCodeTypeConstants.CW_EMAIL_REGISTER_CODE + bo.getEmail());
        if (timeToLive == -2) {
            throw new ServiceException("验证码已失效");
        }
        String code = Convert.toStr(RedisUtils.getCacheObject(EmailCodeTypeConstants.CW_EMAIL_REGISTER_CODE + bo.getEmail()), "");
        if (!code.equals(bo.getCode())) {
            throw new ServiceException("验证码错误!");
        }
        MemberDO memberSort = memberMapper.selectOne(new LambdaQueryWrapper<MemberDO>().orderByAsc(MemberDO::getUserNo).last("LIMIT 1"));
        int userNo = 10000;
        if (memberSort != null) {
            userNo = Convert.toInt(memberSort.getUserNo(), 10000) + 1;
        }
        String externalId = Convert.toStr(SnowflakeIdWorker.generateUniqueId());
        memberDO = new MemberDO();
        memberDO.setExternalId(externalId);
        memberDO.setEmail(bo.getEmail());
        memberDO.setPassword(BCrypt.hashpw(bo.getPassword()));
        memberDO.setInviteCode(this.getInviteCode());
        memberDO.setCreateTime(System.currentTimeMillis());
        memberDO.setEcId("EC" + SnowflakeIdWorker.generateUniqueId());
        memberDO.setUserNo(userNo);
        memberDO.setNickname("ubpay_" + SnCreator.generate8DigitUUIDTo8() + "_" + userNo);
        memberMapper.insert(memberDO);

        /**
         * 创建 公链、加密货币钱包
         */
        CardLinkBodyBO cardLinkBodyBO = new CardLinkBodyBO();
        cardLinkBodyBO.setEmail(bo.getEmail());

        cardLinkBodyBO.setExternalId(externalId);
        Map wallet = leptageService.wallet(cardLinkBodyBO);
        if (wallet != null) {
            String accountId = Convert.toStr(wallet.get("accountId"));
            memberDO.setAccountId(accountId);
            List<CryptoWalletDO> cryptoWalletList = new ArrayList<>();
            List<Map> addresses = (List<Map>) wallet.get("addresses");
            if (addresses != null) {
                for (Map address : addresses) {
                    CryptoWalletDO cryptoWalletDO = new CryptoWalletDO();
                    cryptoWalletDO.setUid(memberDO.getId());
                    cryptoWalletDO.setCcy(Convert.toStr(address.get("ccy")));
                    cryptoWalletDO.setChain(Convert.toStr(address.get("chain")));
                    cryptoWalletDO.setAmount(BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP));
                    cryptoWalletDO.setAddress(Convert.toStr(address.get("address")));
                    cryptoWalletDO.setCreateTime(Convert.toLong(address.get("createdAt")));
                    cryptoWalletDO.setUpdateTime(Convert.toLong(address.get("createdAt")));
                    cryptoWalletList.add(cryptoWalletDO);
                }
                cryptoWalletMapper.insertBatch(cryptoWalletList);
            }

            /**
             * 查询公链币种钱包
             */
            List<Map> balance = leptageService.balance(null, accountId);
            if (balance != null) {
                for (Map map : balance) {
                    cryptoWalletList.forEach(walletItem -> {
                        if (Convert.toStr(map.get("ccy")).equals(walletItem.getCcy()) &&
                            Convert.toStr(map.get("chain")).equals(walletItem.getChain())) {
                            if (Convert.toBigDecimal(map.get("amount")).compareTo(BigDecimal.ZERO) > 0) {
                                // 对匹配到的 CryptoWalletDO 对象进行修改
                                walletItem.setAmount(Convert.toBigDecimal(map.get("amount")));
                            }
                        }
                    });
                }
                cryptoWalletMapper.updateBatchById(cryptoWalletList);
            }
        }

        /**
         * 创建 稳定币钱包
         */
        List<FaitDictDO> faitDictDOS = faitDictMapper.selectList(new LambdaQueryWrapper<FaitDictDO>().eq(FaitDictDO::getStatus, "0").eq(FaitDictDO::getAscription, "php"));
        if (faitDictDOS.size() > 0) {
            List<FaitWalletDO> faitWalletList = new ArrayList<>();
            for (FaitDictDO faitDictDO : faitDictDOS) {
                FaitWalletDO faitWalletDO = new FaitWalletDO();
                faitWalletDO.setUid(memberDO.getId());
                faitWalletDO.setCurrency(faitDictDO.getCurrency());
                faitWalletDO.setAmount(BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP));
                faitWalletDO.setCreateTime(System.currentTimeMillis());
                faitWalletList.add(faitWalletDO);
            }
            faitWalletMapper.insertBatch(faitWalletList);
        }

        /**
         * 处理邀请码
         */
        if (bo.getInviteCode() != null) {
            MemberDO InviteMember = memberMapper.selectOne(new LambdaQueryWrapper<MemberDO>()
                .eq(MemberDO::getInviteCode, bo.getInviteCode()));
            if (InviteMember != null) {
                InviteRecordDO inviteRecordDO = new InviteRecordDO();
                inviteRecordDO.setUid(InviteMember.getId());
                inviteRecordDO.setAccept(memberDO.getId());
                inviteRecordDO.setCreateTime(System.currentTimeMillis());
                inviteRecordMapper.insert(inviteRecordDO);
            }
        }
        return memberMapper.updateById(memberDO) > 0;
    }

    @Override
    public String login(LoginBO bo) {
        if (EmailValidationUtils.isValidEmail(bo.getEmail())) {
            throw new RuntimeException("邮箱格式不正确");
        }
        MemberDO memberDO = memberMapper.selectOne(new LambdaQueryWrapper<MemberDO>()
            .eq(MemberDO::getEmail, bo.getEmail())
            .ne(MemberDO::getStatus, "2"));
        if (memberDO == null) {
            throw new ServiceException("该用户未注册");
        }
        if (memberDO.getStatus().equals("1")) {
            throw new ServiceException("该账号已被禁用，请联系官方客服！");
        }
        if (!BCrypt.checkpw(bo.getPassword(), memberDO.getPassword())) {
            throw new ServiceException("密码错误");
        }
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(memberDO.getId());
        loginUser.setUserType("member");
        LoginHelper.loginByDevice(loginUser, DeviceType.convert("member"));
        return StpUtil.getTokenValue();
    }

    @Override
    public boolean checkEmailUnique(String email) {
        if (EmailValidationUtils.isValidEmail(email)) {
            throw new RuntimeException("邮箱格式不正确");
        }
        MemberDO memberDO = memberMapper.selectOne(new LambdaQueryWrapper<MemberDO>()
            .eq(MemberDO::getEmail, email)
            .ne(MemberDO::getStatus, "2"));
        if (memberDO != null) {
            throw new ServiceException("该邮箱已被注册");
        }
        return true;
    }

    @Override
    public MemberVO getMemberInfo() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException("当前用户退出登录!");
        }
        return memberMapper.selectVoById(loginUser.getUserId());
    }

    @Override
    public boolean editMember(MemberInfoBO bo) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException("当前用户退出登录!");
        }
        MemberDO memberDO = memberMapper.selectById(loginUser.getUserId());
        BeanUtil.copyProperties(bo, memberDO);
        return memberMapper.updateById(memberDO) > 0;
    }

    public String getInviteCode() {
        String inviteCode = RandomUtil.randomNumbers(6);
        MemberDO memberDO = memberMapper.selectOne(new LambdaQueryWrapper<MemberDO>()
            .eq(MemberDO::getInviteCode, inviteCode));
        if (memberDO != null) {
            this.getInviteCode();
        }
        return inviteCode;
    }
}
